#include "preHeader.h"
#include "Session/ClientPacketHandler.h"
#include "Session/CenterSession.h"
#include "Session/SocialServerSession.h"
#include "Session/MapServerMgr.h"
#include "Game/AccountMgr.h"
#include "Game/CharacterMgr.h"
#include "Game/TeleportMgr.h"
#include "Mail/MailMgr.h"
#include "Team/TeamMgr.h"
#include "GameServer.h"
#include "Session/DBPSession.h"

int ClientPacketHandler::HandleCharacterCreate(Account *pAccount, INetPacket &pck)
{
	std::string name;
	uint32 charTypeId;
	uint8 career, gender;
	pck >> name >> charTypeId >> career >> gender;

	GErrorCode err = sCharacterMgr.CheckPlayerName(name);
	if (err != CommonSuccess) {
		pAccount->SendError(err);
		return SessionHandleSuccess;
	}
	const CharPrototype* pProto = GetDBEntry<CharPrototype>(charTypeId);
	if (pProto == NULL || pProto->charRace != (u32)CharRaceType::Player) {
		pAccount->SendError(ParamInvalid);
		return SessionHandleSuccess;
	}

	auto CreateCharacterCallback = [=, uid = pAccount->uid](INetStream& pck) -> GErrorCode {
		int32 errCode;
		std::string_view errMsg;
		pck >> errCode >> errMsg;
		if (errCode != 0) {
			auto pAccount = sAccountMgr.GetAccount(uid);
			if (pAccount != NULL) {
				pAccount->SendError({CommonTransError, errCode, errMsg});
			}
			WLOG("client account`%u` create character, code 1.", uid);
			return CommonSuccess;
		}

		uint32 logicGsID, charId;
		pck >> logicGsID >> charId;
		inst_player_char instInfo;
		instInfo.ipcInstID = charId;
		instInfo.ipcAcctID = uid;
		instInfo.ipcNickName = name;
		instInfo.ipcCareer = career;
		instInfo.ipcGender = gender;
		instInfo.ipcServerID = logicGsID;
		instInfo.ipcAppearance.charTypeId = charTypeId;
		sCharacterMgr.InitPlayerInstance(instInfo);

		NetPacket rpcReqPck(CDBP_NEW_ONE_PLAYER_INSTANCE);
		SaveToINetStream(instInfo, rpcReqPck);
		sDBPSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
			sCharacterMgr.OnCreatePlayerInstance(uid, pck, err);
		}, &sGameServer);

		return CommonSuccess;
	};

	NetPacket rpcReqPck(CCT_CHARACTER_CREATE);
	rpcReqPck << pAccount->logicGsID << pAccount->uid << name;
	sCenterSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
		if (err == RPCErrorNone) {
			CreateCharacterCallback(pck);
		} else {
			WLOG("CenterSession.RPCInvoke() HandleCharacterCreate error[%d].", err);
		}
	}, &sGameServer, DEF_S2S_RPC_TIMEOUT);
	NLOG("client account`%u` create character`%s` ...", pAccount->uid, name.c_str());

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterDelete(Account *pAccount, INetPacket &pck)
{
	uint32 charId;
	pck >> charId;

	auto pChar = sCharacterMgr.GetCharacter(charId);
	if (pChar == NULL || pChar->acct != pAccount->uid) {
		pAccount->SendError(InvalidRequest);
		return SessionHandleSuccess;
	}

	auto DeleteCharacterCallback = [=, uid = pAccount->uid](INetStream& pck)->GErrorCode {
		int32 errCode;
		std::string_view errMsg;
		pck >> errCode >> errMsg;
		if (errCode != 0) {
			auto pAccount = sAccountMgr.GetAccount(uid);
			if (pAccount != NULL) {
				pAccount->SendError({CommonTransError, errCode, errMsg});
			}
			WLOG("client account`%u` delete character, code 1.", uid);
			return CommonSuccess;
		}

		NetPacket rpcReqPck(CDBP_DELETE_ONE_PLAYER_INSTANCE);
		rpcReqPck << charId;
		sDBPSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
			sCharacterMgr.OnDeletePlayerInstance(uid, pck, err);
		}, &sGameServer);

		return CommonSuccess;
	};

	NetPacket rpcReqPck(CCT_CHARACTER_DELETE);
	rpcReqPck << charId;
	sCenterSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
		if (err == RPCErrorNone) {
			DeleteCharacterCallback(pck);
		} else {
			WLOG("CenterSession.RPCInvoke() HandleCharacterDelete error[%d].", err);
		}
	}, &sGameServer, DEF_S2S_RPC_TIMEOUT);
	NLOG("client account`%u` delete character`%s` ...", pAccount->uid, charId);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterList(Account *pAccount, INetPacket &pck)
{
	NetPacket rpcReqPck(CDBP_LOAD_ACCOUNT_PLAYER_PREVIEW_INFO);
	rpcReqPck << pAccount->uid;
	sDBPSession.RPCInvoke(rpcReqPck,
		[uid = pAccount->uid](INetStream& pck, int32 err, bool) {
		if (err != RPCErrorNone) {
			WLOG("HandleCharacterList[%u] failed, %d.", uid, err);
			return;
		}
		auto pAccount = sAccountMgr.GetAccount(uid);
		if (pAccount == NULL) {
			WLOG("HandleCharacterList[%u] failed, null.", uid);
			return;
		}
		NetPacket resp(SMSG_CHARACTER_LIST_RESP);
		resp.Append(pck.GetReadableBuffer(), pck.GetReadableSize());
		pAccount->SendPacket(resp);
		NLOG("client account`%u` get character list successfully.", uid);
	}, &sGameServer);
	NLOG("client account`%u` get character list ...", pAccount->uid);
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterLogin(Account *pAccount, INetPacket &pck)
{
	if (pAccount->IsCharacterOnline()) {
		Character* pChar = pAccount->GetCharacter();
		WLOG("HandleCharacterLogin[%u]: already login charcter[%u,%s]!",
			pAccount->uid, pChar->guid.UID, pChar->name.c_str());
		pAccount->SendRespError(
			SMSG_CHARACTER_LOGIN_RESP, LOGIN_ERROR_CHARACTER_ONLINE);
		return SessionHandleSuccess;
	}

	if (pAccount->isCharLoginning) {
		WLOG("HandleCharacterLogin[%u]: charcter is loginning ...",
			pAccount->uid);
		pAccount->SendRespError(
			SMSG_CHARACTER_LOGIN_RESP, LOGIN_ERROR_CHARACTER_LOGINNING);
		return SessionHandleSuccess;
	}

	uint32 charId;
	pck >> charId;

	const uint32 sn = pAccount->sn;
	const uint32 uid = pAccount->uid;
	pAccount->isCharLoginning = true;

	NetPacket rpcReqPck(CDBP_LOAD_ONE_PLAYER_CHARACTER_INFO);
	rpcReqPck << charId;
	sDBPSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
		Account* pAccount = sAccountMgr.GetAccount(uid);
		if (pAccount != NULL && pAccount->sn == sn && pAccount->isCharLoginning) {
			pAccount->isCharLoginning = false;
		} else {
			WLOG("HandleCharacterLogin[%u->%u] failed, account status invalid.",
				pAccount->uid, charId);
			return;
		}
		if (err != RPCErrorNone) {
			WLOG("HandleCharacterLogin[%u->%u] failed, rpc error %d.",
				pAccount->uid, charId, err);
			pAccount->SendRespError(
				SMSG_CHARACTER_LOGIN_RESP, CommonInternalError);
			return;
		}
		if (pck.IsReadableEmpty()) {
			pAccount->SendRespError(
				SMSG_CHARACTER_LOGIN_RESP, LOGIN_ERROR_CHARACTER_NOT_EXSIT);
			WLOG("HandleCharacterLogin[%u->%u] failed, packet is empty.",
				pAccount->uid, charId);
			return;
		}
		InstPlayerOutlineInfo instInfo;
		LoadFromINetStream(instInfo, pck);
		GErrorCode errCode = sCharacterMgr.UpdateCharacter(instInfo);
		if (errCode != CommonSuccess) {
			pAccount->SendRespError(SMSG_CHARACTER_LOGIN_RESP, errCode);
			WLOG("HandleCharacterLogin[%u->%u] failed, error %d.",
				pAccount->uid, charId, errCode);
			return;
		}
		ObjGUID playerGuid = GetGuidFromLowGuid4Player(charId);
		Character* pChar = sCharacterMgr.GetCharacter(playerGuid);
		if (pChar == NULL) {
			pAccount->SendRespError(
				SMSG_CHARACTER_LOGIN_RESP, CommonInternalError);
			WLOG("HandleCharacterLogin[%u->%u] failed, null.",
				pAccount->uid, charId);
			return;
		}
		pAccount->SendRespError(SMSG_CHARACTER_LOGIN_RESP, CommonSuccess);
		errCode = sCharacterMgr.LoginCharacter(pAccount, pChar);
		if (errCode == CommonSuccess) {
			NLOG("client account`%u` login character[%u,%s] successfully.",
				pAccount->uid, charId, pChar->name.c_str());
		} else {
			WLOG("HandleCharacterLogin[%u->%u] failed, error %d.",
				pAccount->uid, charId, err);
		}
	}, &sGameServer);
	NLOG("client account`%u` login character`%u` ...", pAccount->uid, charId);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterLogout(Account *pAccount, INetPacket &pck)
{
	if (!pAccount->IsCharacterOnline()) {
		WLOG("HandleCharacterLogout[%u]: charcter offline.", pAccount->uid);
		pAccount->SendRespError(
			SMSG_CHARACTER_LOGOUT_RESP, LOGIN_ERROR_CHARACTER_OFFLINE);
		return SessionHandleSuccess;
	}

	pAccount->SendRespError(SMSG_CHARACTER_LOGOUT_RESP, CommonSuccess);
	auto errCode = sCharacterMgr.LogoutCharacter(pAccount, pAccount->GetCharacter());
	if (errCode != CommonSuccess) {
		WLOG("HandleCharacterLogout[%u] failed, error %d.", pAccount->uid, errCode);
	}

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandlePlayerLoadMapFinish(Account *pAccount, INetPacket &pck)
{
	auto pChar = pAccount->GetCharacter();
	NLOG("character[%u,%s] load map finish.", pChar->guid.UID, pChar->name.c_str());
	sTeleportMgr.HandlePlayerLoadMapFinish(pChar->guid);
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamCreate(Account *pAccount, INetPacket &pck)
{
	auto errCode = sTeamMgr.HandleTeamCreate(pAccount->GetCharacter());
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamInvite(Account *pAccount, INetPacket &pck)
{
	ObjGUID targetGuid;
	pck >> targetGuid;
	auto errCode = sTeamMgr.HandleTeamInvite(pAccount->GetCharacter(), targetGuid);
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamInviteResp(Account *pAccount, INetPacket &pck)
{
	uint32 teamId;
	bool isAgree;
	pck >> teamId >> isAgree;
	auto errCode = sTeamMgr.HandleTeamInviteResp(pAccount->GetCharacter(), teamId, isAgree);
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamApply(Account *pAccount, INetPacket &pck)
{
	uint32 teamId;
	pck >> teamId;
	auto errCode = sTeamMgr.HandleTeamApply(pAccount->GetCharacter(), teamId);
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamApplyResp(Account *pAccount, INetPacket &pck)
{
	ObjGUID applicantGuid;
	bool isAgree;
	pck >> applicantGuid >> isAgree;
	auto errCode = sTeamMgr.HandleTeamApplyResp(pAccount->GetCharacter(), applicantGuid, isAgree);
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamLeave(Account *pAccount, INetPacket &pck)
{
	auto errCode = sTeamMgr.HandleTeamLeave(pAccount->GetCharacter());
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamKick(Account *pAccount, INetPacket &pck)
{
	ObjGUID targetGuid;
	pck >> targetGuid;
	auto errCode = sTeamMgr.HandleTeamKick(pAccount->GetCharacter(), targetGuid);
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleTeamTransfer(Account *pAccount, INetPacket &pck)
{
	ObjGUID targetGuid;
	pck >> targetGuid;
	auto errCode = sTeamMgr.HandleTeamTransfer(pAccount->GetCharacter(), targetGuid);
	if (errCode != CommonSuccess) {
		pAccount->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildCreate(Account *pAccount, INetPacket &pck)
{
	std::string guildName;
	pck >> guildName;
	GErrorCode err = sCharacterMgr.CheckGuildName(guildName);
	if (err != CommonSuccess) {
		pAccount->SendError(err);
		return SessionHandleSuccess;
	}

	auto pChar = pAccount->GetCharacter();
	auto TryGuildCreate = [=](Coroutine::YieldContext& ctx) -> GErrorCode {
		NetPacket rpcReqPck(G2M_TRY_GUILD_CREATE);
		sMapServerMgr.RPCInvoke2Player(pChar, rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		auto resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		int32 errCode;
		(*resp.pck) >> errCode;
		if (errCode != CommonSuccess) {
			return (GErrorCode)errCode;
		}

		rpcReqPck.Reset(CGX_TRY_GUILD_CREATE);
		rpcReqPck << pChar->guildId << pChar->guid.UID;
		sSocialServerSession.RPCInvoke(rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		(*resp.pck) >> errCode;
		if (errCode != CommonSuccess) {
			return (GErrorCode)errCode;
		}

		rpcReqPck.Reset(CCT_GUILD_CREATE);
		rpcReqPck << pChar->gsId << pChar->guid.UID << guildName;
		sCenterSession.RPCInvoke(rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		std::string_view errMsg;
		(*resp.pck) >> errCode >> errMsg;
		if (errCode != 0) {
			pChar->SendError({CommonTransError, errCode, errMsg});
			return CommonNoWarning;
		}

		uint32 guildId;
		(*resp.pck) >> guildId;

		rpcReqPck.Reset(CGX_GUILD_CREATE);
		rpcReqPck << pChar->gsId << pChar->guildId << pChar->guid.UID
			<< guildId << guildName;
		sSocialServerSession.RPCInvoke(rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		(*resp.pck) >> errCode;
		if (errCode != CommonSuccess) {
			return (GErrorCode)errCode;
		}

		rpcReqPck.Reset(G2M_GUILD_CREATE_RESP);
		sMapServerMgr.RPCInvoke2Player(pChar, rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		(*resp.pck) >> errCode;
		if (errCode != CommonSuccess) {
			return (GErrorCode)errCode;
		}

		NetPacket toClient(SMSG_GUILD_CREATE_RESP);
		toClient << guildId;
		pChar->SendPacket(toClient);

		return CommonSuccess;
	};

	Coroutine::Spawn([=](Coroutine::YieldContext& ctx) {
		auto errCode = TryGuildCreate(ctx);
		if (errCode != CommonSuccess) {
			pChar->SendError(errCode);
		}
	});

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildInvite(Account *pAccount, INetPacket &pck)
{
	uint32 targetId;
	pck >> targetId;

	auto pChar = pAccount->GetCharacter();
	auto pTarget = sCharacterMgr.GetCharacter(targetId);
	if (pTarget == NULL) {
		return TargetNotExist;
	}
	if (!pTarget->isOnline) {
		return TargetOffline;
	}

	NetPacket trans(CGX_GUILD_INVITE);
	trans << pChar->guildId << pChar->guid.UID
		<< pTarget->guildId << pTarget->guid.UID;
	sSocialServerSession.PushSendPacket(trans);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildInviteResp(Account *pAccount, INetPacket &pck)
{
	uint32 guildId;
	bool isAgree;
	pck >> guildId >> isAgree;
	auto pChar = pAccount->GetCharacter();
	NetPacket trans(CGX_GUILD_INVITE_RESP);
	trans << pChar->guildId << pChar->guid.UID << guildId << isAgree;
	sSocialServerSession.PushSendPacket(trans);
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildApply(Account *pAccount, INetPacket &pck)
{
	uint32 guildId;
	pck >> guildId;
	auto pChar = pAccount->GetCharacter();
	NetPacket trans(CGX_GUILD_APPLY);
	trans << pChar->guildId << pChar->guid.UID << guildId;
	sSocialServerSession.PushSendPacket(trans);
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildApplyResp(Account *pAccount, INetPacket &pck)
{
	uint32 applicantId;
	bool isAgree;
	pck >> applicantId >> isAgree;

	auto pChar = pAccount->GetCharacter();
	auto pApplicant = sCharacterMgr.GetCharacter(applicantId);
	if (pApplicant == NULL) {
		return TargetNotExist;
	}

	NetPacket trans(CGX_GUILD_APPLY_RESP);
	trans << pChar->guildId << pChar->guid.UID
		<< pApplicant->guildId << pApplicant->guid.UID << isAgree;
	sSocialServerSession.PushSendPacket(trans);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildLeave(Account *pAccount, INetPacket &pck)
{
	auto pChar = pAccount->GetCharacter();
	NetPacket trans(CGX_GUILD_LEAVE);
	trans << pChar->guildId << pChar->guid.UID;
	sSocialServerSession.PushSendPacket(trans);
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildKick(Account *pAccount, INetPacket &pck)
{
	uint32 targetId;
	pck >> targetId;

	auto pChar = pAccount->GetCharacter();
	auto pTarget = sCharacterMgr.GetCharacter(targetId);
	if (pTarget == NULL) {
		return TargetNotExist;
	}

	NetPacket trans(CGX_GUILD_KICK);
	trans << pChar->guildId << pChar->guid.UID
		<< pTarget->guildId << pTarget->guid.UID;
	sSocialServerSession.PushSendPacket(trans);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildRise(Account *pAccount, INetPacket &pck)
{
	uint32 targetId;
	int8 guildTitle;
	pck >> targetId >> guildTitle;

	auto pChar = pAccount->GetCharacter();
	auto pTarget = sCharacterMgr.GetCharacter(targetId);
	if (pTarget == NULL) {
		return TargetNotExist;
	}

	NetPacket trans(CGX_GUILD_RISE);
	trans << pChar->guildId << pChar->guid.UID
		<< pTarget->guildId << pTarget->guid.UID << guildTitle;
	sSocialServerSession.PushSendPacket(trans);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildDisband(Account *pAccount, INetPacket &pck)
{
	auto pChar = pAccount->GetCharacter();
	auto TryGuildDisband = [=](Coroutine::YieldContext& ctx) -> GErrorCode {
		NetPacket rpcReqPck(CGX_GUILD_DISBAND);
		rpcReqPck << pChar->guildId << pChar->guid.UID;
		sSocialServerSession.RPCInvoke(rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		auto resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		int32 errCode;
		(*resp.pck) >> errCode;
		if (errCode != CommonSuccess) {
			return (GErrorCode)errCode;
		}

		uint32 guildId;
		(*resp.pck) >> guildId;

		rpcReqPck.Reset(CCT_GUILD_DELETE);
		rpcReqPck << guildId;
		sCenterSession.RPCInvoke(rpcReqPck,
			ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
		resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		std::string_view errMsg;
		(*resp.pck) >> errCode >> errMsg;
		if (errCode != 0) {
			pChar->SendError({CommonTransError, errCode, errMsg});
			return CommonNoWarning;
		}

		return CommonSuccess;
	};

	Coroutine::Spawn([=](Coroutine::YieldContext& ctx) {
		auto errCode = TryGuildDisband(ctx);
		if (errCode != CommonSuccess) {
			pChar->SendError(errCode);
		}
	});

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleGuildGetApply(Account *pAccount, INetPacket &pck)
{
	auto pChar = pAccount->GetCharacter();
	NetPacket trans(CGX_GUILD_GET_APPLY);
	trans << pChar->guildId << pChar->guid.UID;
	sSocialServerSession.PushSendPacket(trans);
	return SessionHandleSuccess;
}
